#include "Mepa.h"
#include <iomanip>

Mepa::Mepa()
{
	
}

Mepa::Mepa(const char* argvInputString, int vSizeP, int vSizeM, int vSizeD)
{	
	this->P.resize(vSizeP);
	this->M.resize(vSizeM,-1);
	this->D.resize(vSizeD,-1);
	this->labels.resize(0);
	
	this->continuar = true;
		
	this->i= 0;
	this->s = -1;
	
	this->pontoMepString = argvInputString;
	
	this->pontoMep.open(argvInputString, std::fstream::in );	
}

Mepa::Mepa(const char* argvInputString, const char* argvOutputString, int vSizeP, int vSizeM, int vSizeD)
{
	this->P.resize(vSizeP);
	this->M.resize(vSizeM,-1);
	this->D.resize(vSizeD,-1);
	this->labels.resize(0);
	
	this->continuar = true;
	
	this->i= 0;
	this->s = -1;
	
	this->pontoMepString = argvInputString;
	this->dumpOutString = argvOutputString;
	
	this->pontoMep.open(argvInputString, std::fstream::in );
	this->dumpOut.open(argvOutputString,std::fstream::out);	
}

Mepa::~Mepa()
{
}

void
Mepa::incrementaI()
{
	this->i++;
}

void
Mepa::setI(int novoI)
{
	this->i = novoI;
}

void
Mepa::crct(int k)
{
	this->s++;
	M[s] = k;
	incrementaI();
}

void
Mepa::crvl(int k, int n)
{
	this->s++;	
	int a = M[D[k] + n];
	M[s] = a; 
	incrementaI();
}

void
Mepa::soma()
{
	M[s-1] = M[s-1] + M[s];
	this->s--;
	incrementaI();
}

void
Mepa::subt()
{
	M[s-1] = M[s-1] - M[s];
	this->s--;
	incrementaI();
}

void
Mepa::mult()
{
	M[s-1] = M[s-1] * M[s];
	this->s--;
	incrementaI();
}

void
Mepa::divi()
{
	M[s-1] = M[s-1] / M[s];
	this->s--;
	incrementaI();
}

void
Mepa::invr()
{
	M[s] = -M[s];
	incrementaI();
}

void
Mepa::conj()
{
	if (M[s-1] == 1 && M[s] == 1)
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::disj()
{
	if (M[s-1]==1 || M[s]==1)
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::nega()
{
	M[s] = 1 - M[s];
	incrementaI();
}

void
Mepa::cmme()
{
	if (M[s-1] < M[s])
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();	
}

void
Mepa::cmma()
{
	if (M[s-1] > M[s])
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::cmig()
{
	if (M[s-1] == M[s])
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::cmdg()
{
	if (M[s-1] != M[s])
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::cmeg()
{
	if (M[s-1] <= M[s])
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::cmag()
{
	if (M[s-1] <= M[s])
		M[s-1] = 1;
	else
		M[s-1] = 0;
	this->s--;
	incrementaI();
}

void
Mepa::armz(int k, int n)
{
	M[D[k]+n] = M[s];
	this->s--;
	incrementaI();
}

void
Mepa::dsvs(int p)
{
	setI(p);
}

void
Mepa::dsvf(int p)
{
	if (M[s]==0)
		setI(p);
	else
		incrementaI();
	this->s--;
}

void
Mepa::nada()
{
	incrementaI();
}

void
Mepa::leit()
{
	s++;
	incrementaI();
	std::cin >> M[s];
}

void
Mepa::impr()
{
	std::cout << M[s];
	this->s--;
	incrementaI();
}

void
Mepa::impl()
{
	std::cout << M[s] << std::endl;
	this->s--;
	incrementaI();
}

void
Mepa::impc()
{
	std::cout << toascii( M[s] ) << std::endl;
	this->s--;
	incrementaI();
}

void
Mepa::inpp()
{
	this->s = -1;
	D[0] = 0;
	incrementaI();
}

void
Mepa::amem(int m)
{
	this->s += m;
	incrementaI();
}

void
Mepa::dmem(int n)
{
	this->s -= n;
	incrementaI();
}

void
Mepa::para()
{
	this->continuar = false;
	cout << "\tINFO: Expressão \"para\" encontrada\n";
}

void
Mepa::chpr(int linha)
{
	this->s++;
	M[s] = i + 1;
	setI(linha);	
}

void
Mepa::enpr(int k)
{
	this->s++;
	M[s] = D[k];
	D[k] = s+1;
	incrementaI();
}

void
Mepa::rtpr(int k, int n)
{	
	D[k] = M[s];
	setI(M[s-1]);
	this->s -= (n + 2);
}

void
Mepa::crvi(int k, int n)
{
	this->s++;
	M[s] = M[ M[D[k] + n] ];
	incrementaI();
}

void
Mepa::armi(int k, int n)
{
	M[ M[D[k]+n] ] = M[s];
	this->s--;
	incrementaI();
}

void
Mepa::cren(int k, int n)
{
	this->s++;
	M[s]= D[k] + n;
	incrementaI();
}

void Mepa::mosm()
{
    if (! this->dumpOut.is_open()) {
        
        char fill = std::cout.fill();
        
        std::cout << "DUMP DE MEMORIA: " << pontoMepString << std::endl;
        cout << "-----------------------------" << endl;
        
        // imprime 'i'
        std::cout << "i: ";
        std::cout << this->i << std::endl;
        
        // imprime 's'
        std::cout << "s: ";
        std::cout << std::setfill( '0' ) << std::setw( 5 );
        std::cout << this->s << std::endl;
        
        // imprime 'D[]'
        for(int i = 0; i < (int) this->D.size(); i++)
        {
            std::cout << "D[" << i << "]: ";
            std::cout << std::setfill( '0' ) << std::setw( 5 );
            std::cout << this->D[i] << std::endl;
        }
        
        std::cout << "---------------" << endl;
        std::cout.fill( fill );
        
        // imprime 'M[]'
        for(int i = 0; i <= this->s; i++)
        {
            std::cout << "[" << i << "] ";
            std::cout << this->M[i] << std::endl;
        }
        
        std::cout << "-----------------------------" << endl;
        incrementaI();
        
    } else {        
        
        char fill = dumpOut.fill();
        
        dumpOut << "DUMP DE MEMORIA: " << this->pontoMepString << std::endl;
        dumpOut << "-----------------------------" << endl;
        
        // imprime 'i'
        dumpOut << "i: ";
        dumpOut << this->i << std::endl;
        
        // imprime 's'
        dumpOut << "s: ";
        dumpOut << std::setfill( '0' ) << std::setw( 5 );
        dumpOut << this->s << std::endl;
        
        // imprime 'D[]'
        for(int i = 0; i < (int) this->D.size(); i++)
        {
            dumpOut << "D[" << i << "]: ";
            dumpOut << std::setfill( '0' ) << std::setw( 5 );
            dumpOut << this->D[i] << std::endl;
        }
        
        dumpOut << "---------------" << endl;
        dumpOut.fill( fill );
        
        // imprime 'M[]'
        for(int i = 0; i <= this->s; i++)
        {
            dumpOut << "[" << i << "] ";
            dumpOut << this->M[i] << std::endl;
        }
        
        dumpOut << "-----------------------------" << endl;
        
        incrementaI();
        
    }
}

void
Mepa::carregaPrograma()
{
	
	/*#### PRIMEIRO LE OS LABELS ####*/
	int linha = 0;	
	struct Label tempLabel;	
	char
	tentaculoD6[6]; /* tentaculo de polvo de 6 ventosas, de 0-5, ventosa <--> char */
	std::string
	instrucao;
	
	/* Este while preenche o vector de labels */	
			while( !this->pontoMep.eof() )
			{				
				/* le do arquivo os 5 primeiros char, o label */
				pontoMep.read( tentaculoD6, 5 );				
				/* insere um caractere de fim de string pra fazer do tentaculoD6 uma 'string' */
				tentaculoD6[5] = '\0';				
				
				/**
				 * Se tentaculoD6 nao for vazio salva o label e sua linha
				 * (strcmp retorna 0 se as strings forem iguais)
				 */
				if ( strcmp( tentaculoD6, "     \0" ) )
				{
					/* construindo  Label temporário */
					strcpy( tempLabel.label, tentaculoD6 );
					tempLabel.linha = linha;
					/* salva o bixo */
					this->labels.push_back( tempLabel );
				}
								
				pontoMep.ignore(1993, '\n');
				linha++;
			} /* fim do while, labels reconhecidos e salvos */
	
	/* traz o cabecote de leitura para o inicio do arquivo */
	/*this->pontoMep.seekg(0, std::ios_base::beg);*/
	/*tive que abrir e fechar */
	this->pontoMep.close();
	this->pontoMep.open(this->pontoMepString, std::fstream::in );	
	linha = 0;	
	
	for(int i=0; i < (int) this->labels.size(); i++)
	{
		cout << this->labels[i].linha << " " << this->labels[i].label << "\n";
	}
	
	/*###### CARREGA EM P[] ######*/
	
	/* variaveis temporarias auxiliares */
	struct LinhaMepa
	tempLinha;	
	char proximo;	
	int arg;
	
		
	/* enquanto nao for o fim do arquivo e tiver memoria de Programa, P */	
	while( (  !pontoMep.eof() ) && (linha <= (int) this->P.size()  ) )
	{
		/* iniciliza o vector de parametros da instrucao */
		tempLinha.args.resize(0);
		/* ignora o espaco reservado para labels */
		this->pontoMep.ignore(6);
		
		/* le e grava o comando em uma linha temporaria 
		 * para que possamos identificar os argumentos*/
		pontoMep >> instrucao;		
		tempLinha.instrucao = instrucao;
		
		/* nestes casos o argumento da instrucao eh um label */
		if ( instrucao == "DSVS" || instrucao == "DSVF" || instrucao == "CHPR" )
		{				
			pontoMep.ignore(1, ' '); // ignora o espaco
			proximo = pontoMep.peek(); // espia o proximo char
			if ( proximo >= '0' && proximo <= '9' ) // se for label numero
			{				
				int numerin;
				pontoMep >> numerin;
				tempLinha.args.push_back(numerin);				
			}
			else													 // se for label alfanumerico
			{
				/* faz label virar string pra comparar */
				pontoMep.read( tentaculoD6, 5 );
				tentaculoD6[5] = '\0';
				
				/* for pra procurar o label */
				for ( int i = 0; i < (int) labels.size(); i++ )
				{
					tempLabel = labels[i];					
					if ( strcmp( tempLabel.label, tentaculoD6 ) == 0 ) //se forem iguais
					{							
						tempLinha.args.push_back( (int) tempLabel.linha);
						break;
					}
				}// fim for busca label
			}// fim if label alfanumerico			
		}// fim instrucao de label
		else 			/* trata instrucoes com 0, 1 ou 2 parametros, integers only */
		{
			pontoMep.ignore(1);
			proximo = pontoMep.peek();
			/* se tiver argumento numero */
			if ( proximo >= '0' && proximo <= '9' && proximo != '\n' )
			{
				pontoMep >> arg;
				tempLinha.args.push_back( arg );
				proximo = pontoMep.peek();
				if ( proximo == ',' )
				{
					pontoMep.ignore(1);
					pontoMep >> arg;
					tempLinha.args.push_back( arg );
				}
			}
		}
		P[linha] = tempLinha;
		/* corre ateh o fim da linha, parando no inicio da "proxima linha", linha++ */
		pontoMep.ignore( 1993, '\n' );
		linha++;
	}// fim while carregaProgama
}// fim carregaProrama

void
Mepa::rodar()
{
	
	while ( this->continuar)
	{
		cout << P[i].instrucao <<endl;
		if ( P[i].instrucao == "AMEM" )
		{
			amem(P[i].args[0]);
			}
			else if ( P[i].instrucao == "ARMI" )
			{
				 armi(P[i].args[0], P[i].args[1]);
			}
			else if ( P[i].instrucao == "ARMZ" )
			{
				 armz(P[i].args[0], P[i].args[1] ); 
			}
			else if ( P[i].instrucao == "CHPR" )
			{
				 chpr(P[i].args[0] ); 
			}
			else if ( P[i].instrucao == "CMAG" )
			{
				 cmag( ); 
			}
			else if ( P[i].instrucao == "CMDG" )
			{
				 cmdg( ); 
			}
			else if ( P[i].instrucao == "CMEG" )
			{
				 cmeg( ); 
			}
			else if ( P[i].instrucao == "CMIG" )
			{
				 cmig( ); 
			}
			else if ( P[i].instrucao == "CMMA" )
			{
				 cmma( ); 
			}
			else if ( P[i].instrucao == "CMME" )
			{
				 cmme( ); 
			}
			else if ( P[i].instrucao == "CONJ" )
			{
				 conj( ); 
			}
			else if ( P[i].instrucao == "CRCT" )
			{
				 crct( P[i].args[0]); 
			}
			else if ( P[i].instrucao == "CREN" )
			{
				 cren(P[i].args[0], P[i].args[1] ); 
			}
			else if ( P[i].instrucao == "CRVI" )
			{
				 crvi( P[i].args[0], P[i].args[1]); 
			}
			else if ( P[i].instrucao == "CRVL" )
			{
				 crvl( P[i].args[0], P[i].args[1]); 
			}
			else if ( P[i].instrucao == "DISJ" )
			{
				 disj( ); 
			}
			else if ( P[i].instrucao == "DIVI" )
			{
				 divi( ); 
			}
			else if ( P[i].instrucao == "DMEM" )
			{
				 dmem(P[i].args[0]); 
			}
			else if ( P[i].instrucao == "DSVF" )
			{
				 dsvf(P[i].args[0] ); 
			}
			else if ( P[i].instrucao == "DSVS" )
			{
				 dsvs(P[i].args[0] ); 
			}
			else if ( P[i].instrucao == "ENPR" )
			{
				 enpr( P[i].args[0]); 
			}
			else if ( P[i].instrucao == "IMPC" )
			{
				 impc( ); 
			}
			else if ( P[i].instrucao == "IMPL" )
			{
				 impl( ); 
			}
			else if ( P[i].instrucao == "IMPR" )
			{
				 impr( ); 
			}
			else if ( P[i].instrucao == "INPP" )
			{
				 inpp( ); 
			}
			else if ( P[i].instrucao == "INVR" )
			{
				 invr( ); 
			}
			else if ( P[i].instrucao == "LEIT" )
			{
				 leit( ); 
			}
			else if ( P[i].instrucao == "MOSM" )
			{
				 mosm( ); 
			}
			else if ( P[i].instrucao == "MULT" )
			{
				 mult( ); 
			}
			else if ( P[i].instrucao == "NADA" )
			{
				 nada( ); 
			}
			else if ( P[i].instrucao == "NEGA" )
			{
				 nega( ); 
			}
			else if ( P[i].instrucao == "PARA" )
			{
				 para( ); 
			}
			else if ( P[i].instrucao == "RTPR" )
			{
				 rtpr( P[i].args[0], P[i].args[1]); 
			}
			else if ( P[i].instrucao == "SOMA" )
			{
				 soma( ); 
			}
			else if ( P[i].instrucao == "SUBT" )
			{
				 subt( ); 
			}
			else
			{
				cout << "\tExecute time error: Instrucao nao encontrada\n";
				exit(1);
			}
	}
	
}


